package icu.javayanglei.file.service.impl

import cn.hutool.core.io.FileUtil
import cn.hutool.core.io.file.FileNameUtil
import cn.hutool.core.lang.Snowflake
import cn.hutool.core.util.ObjectUtil
import icu.javayanglei.file.service.IndexService
import icu.javayanglei.file.vo.CutFileVo
import icu.javayanglei.file.vo.MateFile
import org.springframework.stereotype.Service
import org.springframework.web.multipart.MultipartFile
import java.io.File
import java.io.FileNotFoundException
import java.io.IOException
import java.io.RandomAccessFile
import java.util.*
import java.util.stream.Collectors

@Service
class IndexServiceImpl : IndexService {
    override fun cutFile(file: File, fileSize: Int): CutFileVo {
        val mkdir =
            FileUtil.mkdir(FileUtil.getTmpDirPath() + "/${Snowflake().nextIdStr()}_${FileNameUtil.getPrefix(file)}")
        val size = fileSize * 1024 * 1024;
        val newFile = FileUtil.file(mkdir, FileNameUtil.getName(file));
        FileUtil.copy(file, newFile, true);
        getSplitFile(newFile, size.toLong())
        val splitDir = FileUtil.mkdir("${mkdir}/split");
        val arrayOfFiles = FileUtil.ls(splitDir.absolutePath)
        val list = Arrays.stream(arrayOfFiles).parallel().filter { ObjectUtil.isNotNull(it) }
            .map { MateFile.instanceByFile(it) }.collect(Collectors.toList())
        return CutFileVo(MateFile.instanceByFile(newFile), list);
    }

    /**
     * 文件分割方法
     * @param targetFile 分割的文件
     * @param cutSize 分割文件的大小
     * @return int 文件切割的个数
     */
    override fun getSplitFile(targetFile: File, cutSize: Long): Int {
        //计算切割文件大小
        val count =
            if (targetFile.length() % cutSize == 0L) (targetFile.length() / cutSize).toInt() else (targetFile.length() / cutSize + 1).toInt()
        var raf: RandomAccessFile? = null
        try {
            //获取目标文件 预分配文件所占的空间 在磁盘中创建一个指定大小的文件   r 是只读
            raf = RandomAccessFile(targetFile, "r")
            val length = raf.length() //文件的总长度
            val maxSize = length / count //文件切片后的长度
            var offSet = 0L //初始化偏移量
            for (i in 0 until count - 1) { //最后一片单独处理
                val begin = offSet
                val end = (i + 1) * maxSize
                offSet = getWrite(targetFile.absolutePath, i, begin, end)
            }
            if (length - offSet > 0) {
                getWrite(targetFile.absolutePath, count - 1, offSet, length)
            }
        } catch (e: FileNotFoundException) {
//            System.out.println("没有找到文件");
            e.printStackTrace()
        } catch (e: IOException) {
            e.printStackTrace()
        } finally {
            try {
                raf!!.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
        return count
    }


    override fun mergeFile(files: Array<File>): File {
        val first = files.first()

        // 创建合并的文件夹
        val mkdir =
            FileUtil.mkdir(FileUtil.getTmpDirPath() + "/${Snowflake().nextIdStr()}_${FileNameUtil.getPrefix(first)}")
        // 创建合并的文件
        val mergeFile = FileUtil.file(mkdir, FileNameUtil.getName(first))
        val randomAccessFile = RandomAccessFile(mergeFile, "rw")
        for (i in files.indices) {
            val splitFile = files[i]
            val reader = RandomAccessFile(splitFile, "r")
            val b = ByteArray(1024)
            var n: Int
            //先读后写
            //读
            while (reader.read(b).also { n = it } != -1) {
                //写
                randomAccessFile.write(b, 0, n)
            }
            //合并后删除文件
            FileUtil.del(splitFile)
        }
        return mergeFile;
    }

    override fun mergeFile(files: Array<MultipartFile>): File {
        val multipartFilesToFiles = multipartFilesToFiles(files)
        return mergeFile(multipartFilesToFiles)
    }

    override fun multipartFileToFile(multipartFile: MultipartFile): File {
        val fileName = multipartFile.originalFilename
        // 创建临时文件
        val touch = FileUtil.touch(FileUtil.getTmpDir(), fileName)
        // 拷贝
        FileUtil.writeFromStream(multipartFile.inputStream, touch)
        return touch;
    }

    override fun multipartFilesToFiles(multipartFile: Array<MultipartFile>): Array<File> {
        val toList =
            multipartFile
                .filter { ObjectUtil.isNotNull(it) }
                .map { multipartFileToFile(it) }
                .sortedBy {
                    val name = FileNameUtil.getPrefix(it)
                    val split = name.split("_")
                    split.reversed()[0].toInt()
                }
                .toTypedArray();
        return toList;
    }

    /**
     * 指定文件每一份的边界，写入不同文件中
     * @param file 源文件地址
     * @param index 源文件的顺序标识
     * @param begin 开始指针的位置
     * @param end 结束指针的位置
     * @return long
     */
    fun getWrite(file: String, index: Int, begin: Long, end: Long): Long {
        var endPointer = 0L
        try {
            //申明文件切割后的文件磁盘
            val `in` = RandomAccessFile(File(file), "r")
            //定义一个可读，可写的文件并且后缀名为.tmp的二进制文件
            //读取切片文件
            // 记录切片的文件夹
            val prefix = "${FileNameUtil.getPrefix(file)}_${FileNameUtil.getSuffix(file)}";
            val splitDir = FileUtil.mkdir("${FileUtil.getParent(file, 1)}/split")
            val mFile = File(splitDir.absolutePath, prefix + "_" + index + ".tmp")
            //如果存在
            if (!FileUtil.exist(mFile)) {
                val out = RandomAccessFile(mFile, "rw")
                //申明具体每一文件的字节数组
                val b = ByteArray(1024)
                var n: Int
                //从指定位置读取文件字节流
                `in`.seek(begin)
                //判断文件流读取的边界
                while (`in`.read(b).also { n = it } != -1 && `in`.filePointer <= end) {
                    //从指定每一份文件的范围，写入不同的文件
                    out.write(b, 0, n)
                }

                //定义当前读取文件的指针
                endPointer = `in`.filePointer
                //关闭输入流
                `in`.close()
                //关闭输出流
                out.close()
            } else {
                //不存在
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return endPointer - 1024
    }
}
